package com.alibaba.citrus.ots.platform.enhance.facade.service.diff.sap;

import com.alibaba.acm.shaded.com.google.common.collect.Lists;
import com.alibaba.citrus.common.pair.Pair;
import com.alibaba.citrus.ots.common.annotation.FacadeInvoker;
import com.alibaba.citrus.ots.common.exception.FacadeException;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.DiffCheckRequest;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.constant.DiffResultStatus;
import com.alibaba.citrus.ots.platform.enhance.facade.service.diff.common.constant.DiffTypeEnum;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.constant.DiffStrategyRouteDict;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.dataParser.DataParserManager;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.router.DiffCheckProcessor;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.router.DiffStrategyRoute;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.common.template.StandardTemplateDiff;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.sap.template.Sap006SDODetailStandardTemplate;
import com.alibaba.citrus.ots.platform.enhance.functions.diff.sap.template.Sap006SDOStandardTemplate;
import com.alibaba.citrus.ots.platform.enhance.repository.DiffErrorRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.SalesOrderLineQueryServiceRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.SalesOrderQueryServiceRepository;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.DiffErrorDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.FieldLineDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.SalesOrderDTO;
import com.alibaba.citrus.ots.platform.enhance.repository.model.diff.common.SalesOrderLineDTO;
import com.alibaba.citrus.trail.log.LogEntry;
import com.alibaba.cz.base.tool.log.Log;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.lattice2.epoch.sdk.model.Result;
import com.alibaba.nacos.common.utils.MapUtils;
import com.epoch.app.otsapplicationadaptersdk.sap.MnReceiverCommDetails;
import com.epoch.app.otsapplicationadaptersdk.sap.MnReceiverCommSDO;
import com.epoch.app.otsapplicationadaptersdk.sap.MnSapIf006Request;
import com.epoch.app.otsplatformenhance.model.dto.Sap006DiffRequest;
import com.epoch.app.otsplatformenhance.sap006diffcheck.dto.DiffCheckBySapDataRequest;
import com.epoch.app.otsplatformenhance.sap006diffcheck.service.Sap006DiffCheckService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author hanguo
 * @date 2022/1/25
 * @Description sap006 报文对账
 */
@Service
@Primary
public class Sap006DiffCheckServiceImpl extends AbstractDiffCheck implements Sap006DiffCheckService {


    /**
     * 日志
     */
    private Log log = Log.getLogger(Sap006DiffCheckServiceImpl.class);


    /**
     * 标准对账模板
     */
    @Resource
    private StandardTemplateDiff standardTemplateDiff;

    /**
     * 数据解析器管理类
     */
    @Resource
    private DataParserManager dataParserManager;

    /**
     * 对账策略路由
     */
    @Resource
    private DiffStrategyRoute diffStrategyRoute;

    /**
     * 差错资源类
     */
    @Resource
    private DiffErrorRepository diffErrorRepository;


    @Resource
    private SalesOrderQueryServiceRepository salesOrderQueryServiceRepository;

    @Resource
    private SalesOrderLineQueryServiceRepository salesOrderLineQueryServiceRepository;

    @Override
    @FacadeInvoker
    public Result<String> diffCheckBySapData(DiffCheckBySapDataRequest diffCheckBySapDataRequest) {
        String sap016Data = diffCheckBySapDataRequest.getSap006JsonData();
        MnReceiverCommSDO mnSap006SDO = JSON.parseObject(sap016Data, MnReceiverCommSDO.class);
        List<DiffErrorDTO> diffErrorDTOList = sap006Diff(mnSap006SDO);
        if (CollectionUtils.isEmpty(diffErrorDTOList)) {
            return Result.success("对账完毕，数据一致！");
        } else {
            return Result.fail("", JSON.toJSONString(diffErrorDTOList));
        }
    }


    @Override
    @FacadeInvoker
    public Result<String> diffCheck(Sap006DiffRequest sap006DiffRequest) {
        checkParam(sap006DiffRequest);

        super.doTraverse(sap006DiffRequest.getCondition(),sap006DiffRequest.getStartDate(),
                sap006DiffRequest.getEndDate());
        return Result.success("ture");
    }


    @Override
    void doHandlerLog(Pair<Integer, List<LogEntry>> pair) {
        List<DiffErrorDTO> diffErrorDTOList = new ArrayList<>();
        pair.right().forEach(logEntry -> {
            if (MapUtils.isNotEmpty(logEntry.contents())) {
                String sapMsg = logEntry.contents().get("input");
                log.info("sapMsg={}", sapMsg);
                JSONObject jsonObject = JSON.parseObject(sapMsg);
                String data = jsonObject.getString("body").trim();
                log.info("data={}", data);
                MnSapIf006Request sapIf006Request = JSON.parseObject(data, MnSapIf006Request.class);
                List<MnReceiverCommSDO> sapIf006SDOList = sapIf006Request.getData();
                if (CollectionUtils.isNotEmpty(sapIf006SDOList)) {
                    for (MnReceiverCommSDO sapIf006SDO : sapIf006SDOList) {
                        if(Objects.isNull(sapIf006SDO)){
                            continue;
                        }
                        List<DiffErrorDTO> diffErrorDTOList1 = sap006Diff(sapIf006SDO);
                        if (CollectionUtils.isNotEmpty(diffErrorDTOList1)) {
                            diffErrorDTOList.addAll(diffErrorDTOList1);
                        }
                    }
                }
            }
        });
        if (CollectionUtils.isNotEmpty(diffErrorDTOList)) {
            diffErrorRepository.batchSaveDiffErrorList(diffErrorDTOList);
        }
    }

    public List<DiffErrorDTO> sap006Diff(MnReceiverCommSDO sapIf006SDO) {

        List<DiffErrorDTO> diffErrorDTOList = Lists.newArrayList();
        try {
            //1. 数据抽取
            Map<String, FieldLineDTO> sapIf006SDOMap = dataParserManager.parse(sapIf006SDO);

            //2. 和标准模板进行对比，检查是否缺的问题。 缺失有两个问题： a. 字段缺失， b. 数值缺失
            standardTemplateDiff.check(Sap006SDOStandardTemplate.obtainTemplateFieldSet(),
                    sapIf006SDOMap, Sap006SDOStandardTemplate.obtainIgnoreFieldSet());

            //3. 查询DB获取销售主单
            SalesOrderDTO salesOrderDTO = salesOrderQueryServiceRepository.queryById(sapIf006SDO.getInnerKey());

            if (Objects.isNull(salesOrderDTO)) {
                log.error("未查询到销售主单");
                //埋点
                log.error("Sap006DiffCheckServiceImpl#sap006DiffCheck_fail");
                DiffErrorDTO diffErrorDto = buildDiffErrorDTO(sapIf006SDO, sapIf006SDOMap,
                        null, "未查询到ots侧销售单信息");
                diffErrorDTOList.add(diffErrorDto);
                return diffErrorDTOList;
            }
            //4. 从销售主单上抽取数据
            Map<String, FieldLineDTO> saleOrderDataMap = dataParserManager.parse(salesOrderDTO);
            //5. 和标准模板进行对比，检查是否缺的问题。
            Result<String> checkResult = standardTemplateDiff.check(Sap006SDOStandardTemplate.obtainTemplateFieldSet(), saleOrderDataMap,
                    Sap006SDOStandardTemplate.obtainIgnoreFieldSet());

            if (!checkResult.isSuccess()) {
                //埋点
                log.error("Sap006DiffCheckServiceImpl#sap006DiffCheck_fail");
                DiffErrorDTO diffErrorDTO = buildDiffErrorDTO(sapIf006SDO, sapIf006SDOMap,
                        saleOrderDataMap, checkResult.getMessage());
                diffErrorDTOList.add(diffErrorDTO);
                return diffErrorDTOList;
            }

            //6. 对账
            DiffCheckRequest diffCheckRequest = buildDiffCheckRequest(sapIf006SDOMap, saleOrderDataMap);
            DiffCheckProcessor diffCheckProcessor = diffStrategyRoute.obtainProcessor(diffCheckRequest);
            Result<String> diffResult = diffCheckProcessor.diff(diffCheckRequest);
            if (!diffResult.isSuccess()) {
                //埋点
                log.error("Sap006DiffCheckServiceImpl#sap006DiffCheck_fail");
                DiffErrorDTO diffErrorDto = buildDiffErrorDTO(sapIf006SDO, sapIf006SDOMap,
                        saleOrderDataMap, diffResult.getMessage());
                diffErrorDTOList.add(diffErrorDto);
            }

            List<MnReceiverCommDetails> sapIf006DetailList = sapIf006SDO.getItems();
            if (CollectionUtils.isNotEmpty(sapIf006DetailList)) {
                List<DiffErrorDTO> subDiffErrorDto = sap006DetailDiffCheck(sapIf006SDO.getInnerKey(), sapIf006DetailList);
                if (CollectionUtils.isNotEmpty(subDiffErrorDto)) {
                    diffErrorDTOList.addAll(subDiffErrorDto);
                }
            }
        } catch (Exception e) {
            //埋点
            log.error("Sap006DiffCheckServiceImpl#sap006DiffCheck_fail");
            log.error("sap006主单对账出错了", e);
            DiffErrorDTO diffErrorDto = buildDiffErrorDTO(sapIf006SDO, null,
                    null, "对账出异常了！");
            diffErrorDTOList.add(diffErrorDto);
        }
        return diffErrorDTOList;
    }

    /**
     * sap006销售子单对账
     *
     * @param innerKey
     * @param sapIf006DetailList
     * @return
     */
    private List<DiffErrorDTO> sap006DetailDiffCheck(String innerKey, List<MnReceiverCommDetails> sapIf006DetailList) {

        List<DiffErrorDTO> diffErrorDTOList = Lists.newArrayList();

        //根据销售主单批量查询销售子单
        List<SalesOrderLineDTO> salesOrderLineDTOS = salesOrderLineQueryServiceRepository.querySalesOrderLineByOrderId(innerKey);
        Map<String, SalesOrderLineDTO> salesOrderLineDTOMap = salesOrderLineDTOS.stream()
                .collect(Collectors.toMap(SalesOrderLineDTO::getSerialNumber, Function.identity()));


        for (MnReceiverCommDetails sapIf006Detail : sapIf006DetailList) {
            try {
                Map<String, FieldLineDTO> sapIf006DetailDataMap = dataParserManager.parse(sapIf006Detail);
                standardTemplateDiff.check(Sap006SDODetailStandardTemplate.obtainTemplateFieldSet(), sapIf006DetailDataMap,
                        Sap006SDODetailStandardTemplate.obtainIgnoreFieldSet());
                //3. 获取销售子单
                SalesOrderLineDTO salesOrderLineDTO = salesOrderLineDTOMap.get(sapIf006Detail.getItemKey());

//                if (Objects.isNull(salesOrderLineDTO)) {
//                    log.error("未查询到销售子单");
//                    throw new ProcessException("OTS-13-001-00-16-008");
//                }
                //4. 从销售子单上抽取数据
                Map<String, FieldLineDTO> salesOrderLineDataMap = dataParserManager.parse(salesOrderLineDTO);
                //5. 和标准模板进行对比，检查是否缺的问题。
                Result<String> checkResult = standardTemplateDiff.check(Sap006SDODetailStandardTemplate.obtainTemplateFieldSet(), salesOrderLineDataMap,
                        Sap006SDODetailStandardTemplate.obtainIgnoreFieldSet());

                if (!checkResult.isSuccess()) {
                    //埋点
                    log.error("Sap006DiffCheckServiceImpl#sap006DetailDiffCheck_fail");
                    DiffErrorDTO diffErrorDTO = buildDetailDiffErrorDTO(innerKey, sapIf006Detail, sapIf006DetailDataMap,
                            salesOrderLineDataMap, checkResult.getMessage());
                    diffErrorDTOList.add(diffErrorDTO);
                    continue;
                }

                //6. 对账
                DiffCheckRequest detailDiffCheckRequest = buildDetailDiffCheckRequest(sapIf006DetailDataMap, salesOrderLineDataMap);
                DiffCheckProcessor diffCheckProcessor = diffStrategyRoute.obtainProcessor(detailDiffCheckRequest);

                Result<String> diffResult = diffCheckProcessor.diff(detailDiffCheckRequest);
                if (!diffResult.isSuccess()) {
                    //埋点
                    log.error("Sap006DiffCheckServiceImpl#sap006DetailDiffCheck_fail");
                    DiffErrorDTO diffErrorDTO = buildDetailDiffErrorDTO(innerKey, sapIf006Detail,
                            sapIf006DetailDataMap, salesOrderLineDataMap, diffResult.getMessage());
                    diffErrorDTOList.add(diffErrorDTO);
                }
            } catch (Exception e) {
                //埋点
                log.error("Sap006DiffCheckServiceImpl#sap006DetailDiffCheck_fail");
                log.error("sap006子单对账出错了", e);
                DiffErrorDTO diffErrorDTO = buildDetailDiffErrorDTO(innerKey, sapIf006Detail,
                        null, null, "对账出异常了！");
                diffErrorDTOList.add(diffErrorDTO);
            }
        }
        return diffErrorDTOList;
    }

    /**
     * @param innerKey
     * @param sapIf006Detail
     * @param sapIf006DetailDataMap
     * @param salesOrderLineDataMap
     * @param result
     * @return
     */
    private DiffErrorDTO buildDetailDiffErrorDTO(String innerKey, MnReceiverCommDetails sapIf006Detail, Map<String, FieldLineDTO> sapIf006DetailDataMap, Map<String, FieldLineDTO> salesOrderLineDataMap, String result) {
        Map<String, String> map = new HashMap<>();
        map.put("baseLineData", JSON.toJSONString(sapIf006Detail));

        //1. 封装差错记录
        DiffErrorDTO diffErrorDto = DiffErrorDTO.builder()
                .diffType(DiffTypeEnum.SAP_IF_006_SALES_ORDER_LINE.getDiffType())
                //业务流程描述
                .diffDesc(DiffTypeEnum.SAP_IF_006_SALES_ORDER_LINE.getDiffDesc())
                //对账结果描述
                .result(result)
                //销售主单 ID
                .mainOrderId(innerKey)
//                //销售子单 ID
//                .subOrderId(sapIf017Detail.getOrderLineId())
                //基准数据
                .diffData1(MapUtils.isNotEmpty(sapIf006DetailDataMap) ? JSON.toJSONString(sapIf006DetailDataMap) : null)
                .diffData2(MapUtils.isNotEmpty(salesOrderLineDataMap) ? JSON.toJSONString(salesOrderLineDataMap) : null)
                .strategyRoute(DiffStrategyRouteDict.SAP006DETAIL_DIFF_SALES_ORDER_LINE.getStrategyCode())
                .features(JSON.toJSONString(map))
                .status(DiffResultStatus.FAIL.getStatus()).build();
        return diffErrorDto;
    }

    /**
     * sap销售子单对账
     * <p>
     * 构建子单对账请求
     *
     * @param sapIf006DetailDataMap
     * @param salesOrderLineDataMap
     * @return
     */
    private DiffCheckRequest buildDetailDiffCheckRequest(Map<String, FieldLineDTO> sapIf006DetailDataMap, Map<String, FieldLineDTO> salesOrderLineDataMap) {
        DiffCheckRequest diffCheckRequest = new DiffCheckRequest();
        //对账类型
        diffCheckRequest.setDiffType(DiffTypeEnum.SAP_IF_006_SALES_ORDER_LINE.getDiffType());
        //对账类型描述（就是业务流程描述）
        diffCheckRequest.setDiffDesc(DiffTypeEnum.SAP_IF_006_SALES_ORDER_LINE.getDiffDesc());
        //策略路由
        diffCheckRequest.setStrategyRoute(DiffStrategyRouteDict.SAP006DETAIL_DIFF_SALES_ORDER_LINE.getStrategyCode());
        //基准数据，key 为标准字段
        diffCheckRequest.setBaseLineDataMap(MapUtils.isNotEmpty(sapIf006DetailDataMap) ? sapIf006DetailDataMap : null);
        //被对账数据
        diffCheckRequest.setDiffDataMap(MapUtils.isNotEmpty(salesOrderLineDataMap) ? salesOrderLineDataMap : null);
        return diffCheckRequest;
    }

    /**
     * 构建差错记录对象
     *
     * @param sapIf006SDO
     * @param sapIf006SDOMap
     * @param saleOrderDataMap
     * @param result
     * @return
     */
    private DiffErrorDTO buildDiffErrorDTO(MnReceiverCommSDO sapIf006SDO, Map<String, FieldLineDTO> sapIf006SDOMap, Map<String, FieldLineDTO> saleOrderDataMap, String result) {
        Map<String, String> map = new HashMap<>();
        map.put("baseLineData", JSON.toJSONString(sapIf006SDO));

        //1. 封装差错记录
        DiffErrorDTO diffErrorDto = DiffErrorDTO.builder()
                .diffType(DiffTypeEnum.SAP_IF_006_SALES_ORDER.getDiffType())
                //业务流程描述
                .diffDesc(DiffTypeEnum.SAP_IF_006_SALES_ORDER.getDiffDesc())
                //对账结果描述
                .result(result)
                //存的是销售单 ID
                .mainOrderId(sapIf006SDO.getInnerKey())
                .diffData1(MapUtils.isNotEmpty(sapIf006SDOMap) ? JSON.toJSONString(sapIf006SDOMap) : null)
                .diffData2(MapUtils.isNotEmpty(saleOrderDataMap) ? JSON.toJSONString(saleOrderDataMap) : null)
                .strategyRoute(DiffStrategyRouteDict.SAP006SDO_DIFF_SALES_ORDER.getStrategyCode())
                .features(JSON.toJSONString(map))
                .status(DiffResultStatus.FAIL.getStatus()).build();
        return diffErrorDto;
    }

    /**
     * 主单对账
     *
     * @param sapIf006SDOMap
     * @param saleOrderDataMap
     * @return
     */
    private DiffCheckRequest buildDiffCheckRequest(Map<String, FieldLineDTO> sapIf006SDOMap, Map<String, FieldLineDTO> saleOrderDataMap) {
        DiffCheckRequest diffCheckRequest = new DiffCheckRequest();
        //对账类型
        diffCheckRequest.setDiffType(DiffTypeEnum.SAP_IF_006_SALES_ORDER.getDiffType());
        //对账类型描述（就是业务流程描述）
        diffCheckRequest.setDiffDesc(DiffTypeEnum.SAP_IF_006_SALES_ORDER.getDiffDesc());
        //策略路由
        diffCheckRequest.setStrategyRoute(DiffStrategyRouteDict.SAP006SDO_DIFF_SALES_ORDER.getStrategyCode());
        //基准数据，key 为标准字段
        diffCheckRequest.setBaseLineDataMap(sapIf006SDOMap);
        //被对账数据
        diffCheckRequest.setDiffDataMap(saleOrderDataMap);

        return diffCheckRequest;
    }

    /**
     * 校验参数
     * sap006
     *
     * @param sap006DiffRequest
     */
    private void checkParam(Sap006DiffRequest sap006DiffRequest) {
        if (Objects.isNull(sap006DiffRequest)) {
            throw new FacadeException("OTS-13-001-00-16-003");
        }

        if (Objects.isNull(sap006DiffRequest.getStartDate())) {
            throw new FacadeException("OTS-13-001-00-16-004");
        }

        if (Objects.isNull(sap006DiffRequest.getEndDate())) {
            throw new FacadeException("OTS-13-001-00-16-005");
        }

        if (StringUtils.isBlank(sap006DiffRequest.getCondition())) {
            throw new FacadeException("OTS-13-001-00-16-012");
        }
    }


}
